﻿#pragma once

#include "common.h"
#include "RtAudio.h"

//class RtAudio;
//struct RtAudio::DeviceInfo;

namespace anip
{
	namespace audio
	{
		class Device
		{
			friend class Mixer;
		private:
			Device(const RtAudio::DeviceInfo& d, int i);

		public:
			const int index;
			const string name;
			const int inChannels;
			const int outChannels;
			const bool defaultInput;
			const bool defaultOutput;
		};

		class SoundInstance
		{
			friend class Playback;
		public:
			virtual int channelCount() const = 0;

			virtual void onBegin() {}
			virtual void onSetPosition(unsigned int pos) {}
			virtual bool read(double* dest, unsigned int nSamples, unsigned int start) = 0;
		};

		// Manager of a SoundInstance in the Mixer. Upon calling Mixer::addSound, the
		// ownership of the SoundInstance is transferred to the Playback object. Once
		// the sound has stopped, the Playback object automatically become detached 
		// from Mixer, but the deconstruction of SoundInstance will not occur until
		// the Playback is deconstructed.
		class Playback
		{
			friend class Mixer;
		private:
			unsigned int _pos = 0;
			double* _channelVolume;
			double* _buffer;
			bool _stop = false;
			bool _pause = false;

			Playback(SoundInstance& s);

		public:
			Playback(const Playback& obj) = delete;

			~Playback();

			SoundInstance& sound;
			std::function<void(Playback& self)> onStop;
			std::function<void(Playback& self)> onPlaying;

			double getTimePosition() const;

			void setTimePosition(double t);

			double* volumes() { return _channelVolume; }

			double getVolume() const
			{
				double sum = 0;
				for (int i = 0; i < sound.channelCount(); i++)
					sum += _channelVolume[i];
				return sum / sound.channelCount();
			}

			void setVolume(double v)
			{
				for (int i = 0; i < sound.channelCount(); i++)
					_channelVolume[i] = v;
			}

			bool playing() const { return !_stop && !_pause; }
			bool stopped() const { return _stop; }
			bool paused() const { return _pause; }

			void stop();

			void pause() { _pause = true; }
			void unpause() { _pause = false; }
		};

		class Mixer
		{
			friend class Playback;
		private:
			static RtAudio* _rt;
			static bool _open;
			static unsigned int _channels, _bufferLen, _sampleRate;
			static std::vector<Playback*> _sounds;
			static double _globalVolume;

			static int _callback(void* outputBuffer, void* inputBuffer, unsigned int nFrames,
				double streamTime, unsigned int status, void* userData);

			static void _checkStop();
			
		public:
			static void init();

			static std::vector<Device> devices();

			static void begin(int sampleRate = 44100,
				unsigned int bufferLength = 256);

			static void begin(const Device& d, 
				int sampleRate = 44100, 
				unsigned int bufferLength = 256);

			static void end();

			static double globalVolume() { return _globalVolume; }
			static void setGlobalVolume(double v);

			static Device defaultInput();
			static Device defaultOutput();
			static double getTime();
			static double getLatency();
			static int getSampleRate() { return _sampleRate; }

			static Playback* addSound(
				SoundInstance& sound, 
				double volume = 1);

			static void finalize();
		};
	}
}